package com.gitee.magic.framework.base.context;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.utils.CheckUtils;
import com.gitee.magic.core.utils.StringUtils;
import com.gitee.magic.framework.base.constant.Config;
import com.gitee.magic.framework.base.rest.RequestMethodEnum;
import com.gitee.magic.framework.base.utils.IpUtils;
import com.gitee.magic.framework.head.constants.BaseCode;
import com.gitee.magic.framework.head.exception.BusinessAdviceException;
import com.gitee.magic.framework.head.exception.BusinessException;
import com.gitee.magic.framework.head.utils.CodecUtils;
import com.gitee.magic.framework.head.utils.IoUtils;

import jakarta.servlet.http.HttpServletRequest;

/**
 * @author start
 */
public class Http {

	/**
	 * 访问ID
	 */
	public final static String ACCESSID = "accessid";
	
	/**
	 * 签名值
	 */
	public final static String SIGNATURE = "signature";
	
	/**
	 * 请求到期时间戳（13位）
	 */
	public final static String EXPIRETIME = "expiretime";
	
	private String accessId;
	private String signature;
	private String requestId;
	private String requestBodyEncode;
	
	private boolean fileUpload=false;
	
	/**
	 * 缓存对象
	 */
	private Cache cache=new Cache();
	
	private HttpServletRequest request;
	
	public Http(HttpServletRequest request) {
		this.request = request;
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	/**
	 * 获取ACCESSID
	 */
	public String getAccessId() {
		return accessId;
	}
	
	public void setAccessId(String accessId) {
		this.accessId = accessId;
	}

	/**
	 * 获取签名
	 */
	public String getSignature() {
		if(StringUtils.isEmpty(signature)){
			signature = getRequestParameterValue(SIGNATURE);
			if (StringUtils.isEmpty(signature)) {
				throw new BusinessException(BaseCode.CODE_1001);
			}
		}
		return signature;
	}
	
	public String getRequestIp() {
		return IpUtils.getRequestIp(request);
	}
	
	/**
	 * 当前访问的方法
	 * @return
	 */
	public String getMethod(){
		return request.getMethod();
	}

	/**
	 * 判断当前请求是否为文件上传
	 */
	public boolean isFileUpload() {
		return fileUpload;
	}

	/**
	 * 每次请求唯一日志编号
	 * 
	 * @return
	 */
	public String getRequestId() {
		return requestId;
	}
	
	public String getHeaderValue(String name) {
		String value=getRequest().getHeader(name);
		if(value==null) {
			return value;
		}
		try {
			//header中的参数需要手动解码
			return CodecUtils.decode(value,Config.getEncoding());
		}catch(Exception e) {
			throw new BusinessException(BaseCode.CODE_1008);
		}
	}
	
	public String getParameterValue(String name) {
		//url中的编码会自动解码
		return getRequest().getParameter(name);
	}
	
	public String getRequestParameterValue(String name){
		//先查参数
		String value=getParameterValue(name);
		if(!StringUtils.isEmpty(value)) {
			return value;
		}
		//再查请求头
		return getHeaderValue(name);
	}

	/**
	 * 存入当前请求中的缓存对象
	 */
	public void putCache(Object obj) {
		getCache().put(obj);
	}
	
	/**
	 * 获取当前请求中的缓存对象
	 */
	public <T>T getCache(Class<T> prototype) {
		return getCache().get(prototype);
	}
	
	public Cache getCache() {
		return cache;
	}

	/**
	 * 请求数据编码
	 * @return
	 */
	public String getRequestBodyEncode() {
		if (requestBodyEncode == null) {
			if(getMethod().equals(RequestMethodEnum.GET.name())){
				//按参数名大小排序并整合成key1=va1&key2=val2&key3=val3&key3=val4的格式
				List<String> paramValues=new ArrayList<>();
				Enumeration<String> parameter=getRequest().getParameterNames();
				while(parameter.hasMoreElements()) {
					String name=parameter.nextElement();
					if(!name.equals(SIGNATURE)) {
						String[] values=getRequest().getParameterValues(name);
						List<String> listVal=new ArrayList<>();
						for(String val:values) {
							listVal.add(name+"="+CodecUtils.encode(val,Config.getEncoding()));
						}
						Collections.sort(listVal);
						paramValues.add(StringUtils.listToString(listVal,"&"));
					}
				}
				Collections.sort(paramValues);
				requestBodyEncode=StringUtils.listToString(paramValues,"&");
			}else if(getMethod().equals(RequestMethodEnum.POST.name())||
					getMethod().equals(RequestMethodEnum.PUT.name())||
					getMethod().equals(RequestMethodEnum.DELETE.name())){
				if (isFileUpload()) {
					throw new ApplicationException("当前主体数据为文件上传流读取异常");
				}
				requestBodyEncode=getRequestBody();
			}
		}
		return requestBodyEncode;
	}
	
	public String getRequestBody() {
		try {
			return new String(IoUtils.inputStreamConvertBytes(getRequest().getInputStream(),getRequest().getContentLength()));
		} catch (IOException e) {
			throw new BusinessAdviceException(e, BaseCode.CODE_1010);
		}
	}
	
	/**
	 * 设置文件上传请求的主体内容
	 */
	public void setUploadRequestBody(String requestBody) {
		if (isFileUpload()) {
			this.requestBodyEncode = requestBody;
		}
	}
	
	/**
	 * 请求校验 步骤： 
	 * 1、调用requestCheck
	 * 2、调用签名方法(signaturesHmacSHA1Check)
	 */
	public void requestCheck() {
		requestCheck(true);
	}

	/**
	 * 请求校验 步骤： 
	 * 1、调用requestCheck
	 * 2、调用签名方法(signaturesHmacSHA1Check)
	 */
	public void requestCheck(boolean checkParameter) {
		this.fileUpload = false;
		requestCheck(false, Config.getMaxBodyTextSize(),0L,checkParameter);
	}

	/**
	 * 文件上传请求方式检测
	 */
	public void uploadRequestCheck() {
		uploadRequestCheck(0L);
	}

	/**
	 * 文件上传请求方式检测
	 */
	public void uploadRequestCheck(long checkLength) {
		uploadRequestCheck(Config.getMaxBodyUploadFileSize(),true);
	}

	/**
	 * 文件上传请求方式检测
	 */
	public void uploadRequestCheck(long checkLength,boolean checkParameter) {
		this.fileUpload = true;
		requestCheck(true, Config.getMaxBodyUploadFileSize(), checkLength,checkParameter);
	}

	/**
	 * 请求检测
	 * @param isBodyNotNull
	 *            请求内容是否可以为空
	 * @param maxLength
	 *            请求的内容长度
	 * @param checkLength
	 *            校验内容的长度
	 * @return
	 */
	private void requestCheck(boolean isBodyNotNull, long maxLength, long checkLength,boolean checkParameter) {

		if(checkParameter) {
			//访问ID
			accessId = getRequestParameterValue(ACCESSID);
			if (StringUtils.isEmpty(accessId)) {
				throw new BusinessException(BaseCode.CODE_1006);
			} else if (!CheckUtils.isMd5(accessId)) {
				throw new BusinessException(BaseCode.CODE_1007);
			}
			
			String time = getRequestParameterValue(EXPIRETIME);
			if (StringUtils.isEmpty(time)) {
				//
			}else {
				//如果传入请求时间则验证有效性
				if(!CheckUtils.isLong(time)) {
					throw new BusinessException(BaseCode.CODE_1004);
				}
				long expiretime=Long.parseLong(time);
				long currentTime=System.currentTimeMillis();
				if(currentTime>expiretime) {
					throw new BusinessException(BaseCode.CODE_1005);
				}
			}
		}
		
		//请求日志编号
		this.requestId = HttpHolder.getRequestId(request);
		
		if(RequestMethodEnum.POST.name().equals(getMethod())||
				RequestMethodEnum.PUT.name().equals(getMethod())||
				RequestMethodEnum.DELETE.name().equals(getMethod())){
			// 主体内容的大小
			long contentLength = getRequest().getContentLength();
			// 请求主体是否可以为空
			if (isBodyNotNull&&contentLength == 0) {
				throw new BusinessException(BaseCode.CODE_1014);
			}
			if (contentLength > maxLength) {
				throw new BusinessException(BaseCode.CODE_413);
			}
			if (checkLength > 0) {
				if (contentLength != checkLength) {
					throw new BusinessException(BaseCode.CODE_1013);
				}
			}
		}
		
	}

	/**
	 * 对主体数据进行签名校验
	 */
	public void signaturesHmacSha1Check(String key) {
		if (!CodecUtils.signatureHmacSha1(key, getRequestBodyEncode()).equals(getSignature())) {
			throw new BusinessException(BaseCode.CODE_1002);
		}
	}

}